home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Development Platforms / LISP Related / U. Mass AI & LISP Tools / MODULES / SM / SM.lisp < prev    next >
Encoding:
Text File  |  1990-06-25  |  85.1 KB  |  1,800 lines  |  [TEXT/MACA]

  1. ; (c) Copyright 1990 by University of Massachusetts. All rights reserved.
  2. ; This software was conceived, designed, and written by Dan Suthers 
  3. ; while supported by the National Science Foundation under grant number
  4. ; MDR 8751362, and by a fellowship from Apple Computer, Inc., Cupertino,
  5. ; CA.  Partial support was also received from the Office of Naval Research
  6. ; under a University Research Initiative Grant, contract N00014-86-K-0764.
  7. ; Mr. Suthers created this software under his own initiative while in an 
  8. ; academic relationship with the University of Massachusetts.  The above
  9. ; copyright notice was a condition placed by University lawyers on approval
  10. ; of distribution of this software by Apple Computer, and is not meant to
  11. ; imply that this software was created in an employment or "work for hire"
  12. ; relationship between the University and Mr. Suthers.
  13. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  14. ;
  15. ; File:         SM.LISP
  16. ; Author:       Dan Suthers
  17. ; Created:      07-Nov-87 00:38:00 (Dan Suthers)
  18. ; Modified:     22-Jun-90 02:11:42 (Dan Suthers)
  19. ; Language:     LISP
  20. ; Package:      SM
  21. ;
  22. ; Description:  Structure Manager.
  23. ;  
  24. ;               Common Lisp Structures are extended to include support
  25. ;               for keeping track of structure types defined and of 
  26. ;               instances of them created; creating, destroying, and 
  27. ;               printing instances and types; recording and accessing
  28. ;               information about the slots; and reuse of structures of
  29. ;               destroyed instances to reduce garbage collection. 
  30. ;
  31. ; (c) Copyright 1988, by Daniel D. Suthers
  32. ;                        Department of Computer and Information Science
  33. ;                        University of Massachusetts
  34. ;                        Amherst, Massachusetts 01003
  35. ;
  36. ; This software was conceived, designed, and written by Dan Suthers 
  37. ; while supported by the National Science Foundation under grant number
  38. ; MDR 8751362, and by a fellowship from Apple Computer, Inc., Cupertino,
  39. ; CA.  Partial support was also received from the Office of Naval Research
  40. ; under a University Research Initiative Grant, contract N00014-86-K-0764.
  41. ; I wish to acknowledge the generous support of Beverly Woolf, who obtained 
  42. ; the above grants and encouraged me to pursue my own research interests in
  43. ; her lab.  This work would not have been possible without the resources and
  44. ; stimulating environment of the Computer and Information Science department.
  45. ;
  46. ; Permission to use, modify, and distribute this software is granted subject 
  47. ; to the following restrictions and understandings:
  48. ; 1. The file header, including this notice, shall be retained, and may be
  49. ;    extended to include documentation of modifications to the software.
  50. ; 2. This material is for nonprofit educational and research purposes only.
  51. ;    Users are requested, but not required, to inform Mr. Suthers of any 
  52. ;    noteworthy uses of this software.
  53. ; 3. Mr. Suthers and the University of Massachusetts make no warrantee or
  54. ;    representation that the operation of this software will be error free,
  55. ;    and are under no obligation to provide any services.
  56. ; 4. Any user of such software agrees to indemnify and hold harmless Mr.
  57. ;    Suthers and the University of Massachusetts from all claims arising 
  58. ;    out of the use or misuse of this software, or arising out of any 
  59. ;    accident, injury, or damage whatsoever, and from all costs, counsel
  60. ;    fees, and liabilities incurred in or about any such claim, action, or
  61. ;    proceeding brought thereon.
  62. ; 5. All materials and reports developed as a consequence of the use of 
  63. ;    this software shall duly acknowledge such use, in accordance with
  64. ;    the usual standards of acknowledging credit in academic research.
  65. ;
  66. ; Status:       In a usable state. Most recent testing:
  67. ;               Hewlett Packard 9000       02-Nov-88 Dan Suthers
  68. ;               Macintosh II Coral/Allegro 14-Sep-89 Dan Suthers 
  69. ;               Texas Instruments Explorer 02-Nov-88 Dan Suthers
  70. ;               VAX/VMS                    02-Nov-88 Dan Suthers
  71. ;
  72. ; Changes:      
  73. ;   10-May-88 Changing slots of the $structure-type$ structure to record type 
  74. ;     information usable elsewhere, and added macros to access this information.
  75. ;     Used to rewrite faster prints.  Improved readability and efficiency of DST, 
  76. ;     including replacing make-<type> with BOA allocate-<type> for efficiency.
  77. ;   12-May-88 Bug in HP CL requires placing :constructor in current package.
  78. ;   22-May-88 save-type and load-type moved here from SMEDIT. Now save-type has 
  79. ;     define-type and compile args. Got rid of "-struct-" in names.
  80. ;   23-May-88 Added type declarations. 
  81. ;   17-Jun-88 Documentation accessable in *-----SM-----*
  82. ;   20-Jun-88 Added SLOT-TYPES (recording and access to the declared :type).
  83. ;   24-Jun-88 Machine Specific Patches:
  84. ;     #+TI Explorer: DST went into recursive dive. The macro expansion of DST 
  85. ;          normally contains the original (quoted) list structure of the original 
  86. ;          DST call.  The TI appeared to invoke a recursive expansion of the DST 
  87. ;          expression, even though it was quoted.  Copying the list solved it.
  88. ;     #+VAX: Open-file cannot :create when :io and file does not exist. :Output 
  89. ;          works. (CCL needs :IO for tabbing.)
  90. ;   25-Jun-88 Testing : Type declarations in create-<type> fixed; added type 
  91. ;     checking to <type> macro. Confirmed that most implementations can only use
  92. ;     DST at top level in compiled or preprocessed code.
  93. ;   29-Jun-99 Save-Instances added to save specified instances.
  94. ;   02-Jul-88 DST now signals continuable error if redefining, since this
  95. ;     clobbers all instances (dangerous). Added COPYS.
  96. ;   14-Jul-88 Added DEFINE-TYPE.  Both it and DST now can :redefine, and
  97. ;     their type option syntax is compatible with defstruct. Added :style
  98. ;     :list-macro to prints.  :TYPE, :INITIAL-OFFSET, and :NAMED now work.
  99. ;     Destroy-type now undefines functions. Renamed COPYS to COPIES.
  100. ;   19-Jul-88 ALLOCATE-<type> now in calling package instead of SM, to avoid
  101. ;      collisions if type of same name defined in several packages.  Renamed
  102. ;      to SM$ALLOCATE-<type> to avoid collision in calling package.
  103. ;   27-Jul-88 CCL :capitalize tried to capitalize "" and died on char ref 0.
  104. ;      Bypassed by changing ~A to ~S for all slot values in PRINTS.
  105. ;   30-Jul-88 Added proclamations for prints, save-type, and load-type.
  106. ;   03-Aug-88 :sort-instances option added to DST; misc other stuff.
  107. ;   23-Oct-88 SAVE-TYPE now pretty prints type options when saving 
  108. ;     definition; in :CCL, compilation of saved files is eval-enqueued.
  109. ;     Huge documentation string no longer loaded.
  110. ;   01-Nov-88 TYPE-INFO and SLOT-INFO have alternate expansions depending
  111. ;     on number of arguments given: more args select info from the alist.
  112. ;     Structure-types list kept sorted.
  113. ;   13-Nov-88 Slots printed in order the user defined them (no longer sorted).
  114. ;   16-Nov-88 SAVE-TYPE now has :init-forms and :append arguments.
  115. ;   31-Dec-88 SLOT-INFO and TYPE-INFO now CDR-ASSOC instead of just ASSOC
  116. ;     when optional args are given. :COMMENTS type option now recorded as
  117. ;     structure's documentation.  COPIES now has copy-tree keyword argument,
  118. ;     which defaults T, and copies slot values which are conses.
  119. ;   14-Sep-89 LOAD-TYPE modified to run :AFTER-LOAD method if specified as
  120. ;     a type option.
  121. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  122. #| About the Structure Manager:
  123.  
  124. Common Lisp Structures are extended to include support for:
  125. - keeping track of structure types defined and of instances of them created;
  126. - creating, destroying, and printing instances and types;
  127. - recording and accessing information about the type and slots definitions; 
  128. - reuse of structures of destroyed instances to reduce garbage collection;
  129. - saving to and reloading definitions and instances from files; and 
  130. - re-representation of existing instances when a type is redefined.
  131.  
  132. With the exception of indirect reference to structures through names, SM
  133. attempts to be compatible with syntax of DEFSTRUCT and related functions,
  134. and to provide at least as much functionality.  Currently only the :include
  135. and :conc-name functionality of DEFSTRUCT are not supported.
  136.  
  137. As a general rule, we define for each type only those functions which need 
  138. type-specific arguments.  The other functions are shared between types.
  139. Most applications will primarily be concerned with DST, CREATE-<type>, 
  140. <type>, INSTANCES, GETS, DESTROYS, PRINTS, and the structure access 
  141. functions.  See these first.
  142.  
  143. TYPE DEFINITIONS:
  144.  
  145.   DST (macro) and DEFINE-TYPE (function) define types.  See documentation of
  146.   DST: its syntax is similar to defstruct.  Existing types may be redefined.
  147.   STRUCTURE-TYPES returns a list of all defined types.
  148.  
  149.   WARNING: DST can only be used at top level on some machines. It expands into:
  150.     (PROGN <check-for-redefinition> 
  151.            <record-type-definition> 
  152.            (DEFSTRUCT <type+options> ...)
  153.            (DEFUN CREATE-<type> ...)
  154.            (DEFMACRO <type> ...)
  155.            '<type>)
  156.   When not at top level, the preprocessor tries to expand SETFs in CREATE-<type>.
  157.   When the type is :reusable, these include SETFs to <type>-<slot> functions to
  158.   be defined by DEFSTRUCT.  However, at preprocess time, the DEFSTRUCT has not
  159.   been evaluated, even though it occurs before the CREATE-<type> definition. So 
  160.   you get a '<type>-<slot> has no setf method' error.  This is not a problem 
  161.   with the DEFINE-TYPE function, which may be used when not at top level.
  162.  
  163. TYPE SPECIFIC FUNCTIONS:
  164.  
  165.   All standard structure-specific functions defined by DEFSTRUCT are defined in
  166.   the calling package, and have the usual behavior.  The following are also
  167.   defined:
  168.  
  169. <type> <name> &key <slot> ...                               [No-Eval Macro]
  170.  
  171.   Instances of the type may be created with this no-eval macro, where
  172.   the <name> is an atom which this instance is accessed with, and the 
  173.   keyword arguments correspond to the slot names of uncomputed slots of
  174.   the structure.  The keyword defaults are the same as in the structure
  175.   definition. This actually expands into a CREATE-<type> function call:
  176.  
  177. CREATE-<type> <name> &optional <slot> ...                        [Function]
  178.  
  179.   Creates an instance of structure type <type>, and indexes the instance
  180.   under <name>.  No defaults are available on the slot arguments.  This 
  181.   is intended for 'internal' use by code: hence we trade off the con-
  182.   venience of default values and keyword arguments for the efficiency of 
  183.   explicit  arguments.  The slot arguments are in the order they were 
  184.   given in DST, except that :computed slots have no argument.
  185.  
  186. INSTANCE FUNCTIONS AND MACROS:
  187.  
  188.   See documentation for COPIES, DESTROYS, GETS, and PRINTS.
  189.  
  190. TYPE ACCESS MACROS:
  191.  
  192.   For getting information about types, see COMPUTED-SLOTS, CREATOR,
  193.   DEFINING-FORM, INITIAL-OFFSET,  INSTANCES, NAMED, READ-ONLY-SLOTS, 
  194.   REPRESENTATION, REUSABLE, SLOT-ACCESS, SLOT-DEFAULTS, SLOT-INFO, 
  195.   TYPE-INFO, and UNCOMPUTED-SLOTS. TYPE-INFO is setf-able.
  196.  
  197. GLOBAL OPERATIONS ON TYPES:
  198.  
  199.   See DESTROY-ALL-TYPES, DESTROY-TYPE, FLUSH-FREELIST, RESET-ALL-TYPES,
  200.   and RESET-TYPE.  DST and DEFINE-TYPE can redefine an existing type.
  201.  
  202. |#
  203. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  204. ; Exercizes for the macho reader:
  205. ; 1. Play with :include option and get the code to handle it.
  206. ; 2. Ditto for :conc-name.
  207. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  208.  
  209. (in-package 'SM :use '("LISP"))
  210.  
  211. (export '(
  212.           *default-instance-file-path*
  213.           *default-instance-file-type*
  214.           *warn-of-redefinitions*
  215.  
  216.           ;; Defining 
  217.           dst
  218.           define-type
  219.  
  220.           ;; For instances
  221.  
  222.       copies
  223.           destroys
  224.           gets
  225.           prints
  226.  
  227.           ;; For types
  228.  
  229.           computed-slots
  230.           creator
  231.           defining-form
  232.           destroy-type
  233.           flush-freelist
  234.           named
  235.           initial-offset
  236.           instances
  237.           load-type
  238.           read-only-slots
  239.           representation
  240.           reset-type
  241.           reusable
  242.           save-instances
  243.           save-type
  244.           slot-access
  245.           slot-defaults
  246.           slot-info
  247.           slot-types
  248.           type-info
  249.           uncomputed-slots
  250.  
  251.           ;; For SM as a whole
  252.  
  253.           destroy-all-types
  254.           reset-all-types
  255.           structure-types
  256.           ))
  257.  
  258. ;;; The best default optimization for Coral Allegro and HP Common Lisp.
  259. ;;; Crank speed up to 3 once past argument checking.
  260. (proclaim '(optimize (safety 1) (space 2) (speed 2)))
  261.  
  262. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  263. ;;;
  264. ;;;                       INTERNAL DATA STRUCTURES
  265. ;;;
  266. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  267.  
  268. (defparameter *DEFAULT-INSTANCE-FILE-PATH* 
  269.     #+HP "$HOME/"
  270.         #+:CCL "ccl;"
  271.         #+VAX "sys$login:"
  272.         #+TI   "*;"
  273.         #-(or hp :ccl vax ti) ""
  274.   "Directory where instance definitions are saved and loaded by default.")
  275. (proclaim '(string *default-instance-file-path*))
  276.  
  277. (defparameter *DEFAULT-INSTANCE-FILE-TYPE* 
  278.     #+HP "l"
  279.         #+:CCL "lisp"
  280.         #+VAX "lsp"
  281.         #+TI   "lisp"
  282.         #-(or hp :ccl vax ti) "lsp"
  283.   "Default extension for files instances are saved in and loaded from.")
  284. (proclaim '(string *default-instance-file-type*))
  285.  
  286. #-TI (defparameter *KEYWORD-PACKAGE* (find-package "KEYWORD")) ; exists on TI
  287. #-TI (proclaim '(package *keyword-package*))
  288.  
  289. (defparameter *SM-PACKAGE* (find-package "SM"))
  290. (proclaim '(package *sm-package*))
  291.  
  292. ;;; Each structure type has one of the following structure-type structures
  293. ;;; as its $structure-type$ property.
  294.  
  295. (defstruct (STRUCTURE-TYPE (:constructor 
  296.                             record-new-type
  297.                             (reusable         slot-access     macro-access 
  298.                              uncomputed-slots computed-slots  read-only-slots 
  299.                              slot-defaults    slot-types      slot-info
  300.                              representation   initial-offset  named
  301.                              creator          defining-form   info)))
  302.  
  303.   ;; list of names of created instances
  304.   (INSTANCE-NAMES    nil :type list)
  305.  
  306.   ;; Flag indicating whether the freelist is used, and the freelist of old
  307.   ;; instance structures to reuse.
  308.   (REUSABLE          nil :type symbol :read-only t)
  309.   (FREELIST          nil :type list)
  310.  
  311.   ;; PRINTS drivers: ordered lists of (slotname . slotaccess) for all slots
  312.   ;; and macro slots.  Also usable elsewhere to associate slot names to access.
  313.   (SLOT-ACCESS       nil :type list   :read-only t)
  314.   (MACRO-ACCESS      nil :type list   :read-only t)
  315.  
  316.   ;; Provided for other programs.  Uncomputed-slots is in original order, 
  317.   ;; so it is in order of arguments to create-<type>.
  318.   (UNCOMPUTED-SLOTS  nil :type list   :read-only t)
  319.   (COMPUTED-SLOTS    nil :type list   :read-only t)
  320.   (READ-ONLY-SLOTS   nil :type list   :read-only t)
  321.   (SLOT-DEFAULTS     nil :type list   :read-only t) ; ((<name> . <default>)*)
  322.   (SLOT-TYPES        nil :type list   :read-only t) ; ((<name> . <type>)*)
  323.   (SLOT-INFO         nil :type list   :read-only t) ; ((<name> . <alist>)*)
  324.                                                       ; alist: user keys -> info
  325.   ;; Record of standard defstruct options.
  326.   (REPRESENTATION    nil :type T      :read-only t) ; nil, list, vector, (vector <type>)
  327.   (INITIAL-OFFSET    0   :type fixnum :read-only t) ; valid only if above non-nil
  328.   (NAMED             t   :type symbol :read-only t) ; T or nil
  329.  
  330.   ;; Create-<type> function name.
  331.   (CREATOR           nil :type symbol :read-only t)
  332.  
  333.   ;; The body of the DST macro call, saved to write out to files, etc.
  334.   (DEFINING-FORM     nil :type list :read-only t)
  335.  
  336.   ;; User may do anything with this slot.
  337.   (INFO              nil)
  338.   )
  339.  
  340. (defparameter *STRUCTURE-TYPES* nil) ; Names of defined struct types.
  341. (proclaim '(list *structure-types*))
  342.  
  343. (defparameter *WARN-OF-REDEFINITIONS* T 
  344.   "Toggles whether DST and create-<type> print warnings on redefinitions.")
  345.  
  346. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  347. ;;;
  348. ;;;                  MACROS FOR STRUCTURE TYPE ACCESS
  349. ;;;
  350. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  351. ;;; Type macros.  All exported macros except INFO access :read-only slots,
  352. ;;; so these are safe against illicit attempts at setf.  Type checking:
  353. ;;; CHECK-TYPE would be more appropriate but it would complicate and slow 
  354. ;;; down the macros. THE does check interactively on some machines.
  355.  
  356. (defmacro COMPUTED-SLOTS (type)
  357.   "computed-slots <type>                                               [Macro]
  358.   Returns list of slot names which are computed for <type>.  These are
  359.   NOT specifiable as arguments to create-<type> or the <type> macro."
  360.   `(structure-type-computed-slots 
  361.     (the structure-type (get ,type '$structure-type$))))
  362.  
  363. (defmacro CREATOR (type)  
  364.   "creator <type>                                                      [Macro]
  365.   Returns the name of the create-<type> function for <type>."
  366.   `(structure-type-creator
  367.     (the structure-type (get ,type '$structure-type$))))
  368.  
  369. (defmacro DEFINING-FORM (type)
  370.   "defining-form <type>                                                [Macro]
  371.   Returns the expression which defined the type."
  372.   `(structure-type-defining-form 
  373.     (the structure-type (get ,type '$structure-type$))))
  374.  
  375. (defmacro FREELIST (type)
  376.   ;; Writeable slot, not exported.  World doesn't need this.
  377.   `(structure-type-freelist
  378.     (the structure-type (get ,type '$structure-type$))))
  379.  
  380. (defmacro INITIAL-OFFSET (type)
  381.   "initial-offset <type>                                               [Macro]
  382.   Returns the value given to :initial-offset.  See also representation macro."
  383.   `(structure-type-initial-offset
  384.      (the structure-type (get ,type '$structure-type$))))
  385.  
  386. (defmacro INSTANCE-NAMES (type)
  387.   ;; Writeable slot, so not exported.  World uses (instances <type>) instead.
  388.   `(structure-type-instance-names 
  389.     (the structure-type (get ,type '$structure-type$))))
  390.  
  391. (defmacro MACRO-ACCESS (type)
  392.   ;; Not exported.  World uses slot-access.
  393.   `(structure-type-macro-access 
  394.     (the structure-type (get ,type '$structure-type$))))
  395.  
  396. (defmacro NAMED (type)
  397.   "named     <type>                                                    [Macro]
  398.   Returns T if <type> is represented with named structures, else NIL."
  399.   `(structure-type-named
  400.      (the structure-type (get ,type '$structure-type$))))
  401.  
  402. (defmacro READ-ONLY-SLOTS (type)
  403.   "read-only-slots <type>                                              [Macro]
  404.   Returns list of slot names which are :read-only for <type>."
  405.   `(structure-type-read-only-slots 
  406.      (the structure-type (get ,type '$structure-type$))))
  407.  
  408. (defmacro REPRESENTATION (type)
  409.   "representation <type>                                               [Macro]
  410.   Returns NIL if the default structure representation was used, or LIST,
  411.   VECTOR, or (VECTOR <element-type>) otherwise -- see CLtL p. 314."
  412.   `(structure-type-representation
  413.      (the structure-type (get ,type '$structure-type$))))
  414.  
  415. (defmacro REUSABLE (type)
  416.   "reusable <type>                                                     [Macro]
  417.   T iff memory of destroyed instances is reused for new instances of <type>."
  418.   `(structure-type-reusable 
  419.     (the structure-type (get ,type '$structure-type$))))
  420.  
  421. (defmacro SLOT-ACCESS (type)
  422.   "slot-access <type>                                                  [Macro]
  423.   Returns a-list of (<slot-name> . <slot-access-function>) pairs for <type>."
  424.   `(structure-type-slot-access 
  425.     (the structure-type (get ,type '$structure-type$))))
  426.  
  427. (defmacro SLOT-DEFAULTS (type)
  428.   "slot-defaults <type>                                                [Macro]
  429.   Returns a-list of (<slot-name> . <slot-default-expression>) pairs for <type>.
  430.   NOTE that <slot-default-expression> must be EVALUTED to guarantee a correct
  431.   value: this is an alist to the EXPRESSIONS which produce the defaults."
  432.   `(structure-type-slot-defaults
  433.     (the structure-type (get ,type '$structure-type$))))
  434.  
  435. (defmacro SLOT-INFO (type &optional slot keyword)
  436.   "slot-info <type> &optional <slot> <keyword>                         [Macro]
  437.   Access to an association list of slot names to info lists. Each info list 
  438.   is itself an association list of keywords to info.  That is, the top level
  439.   list looks like: 
  440.     ((<slot-1> . ((<key> . <info>)*)) ... (<slot-N> . ((<key> . <info>)*)))
  441.   where <key> is a user-supplied slot definition keyword, and <info> is the
  442.   info which DST found after it.  The expansion depends on whether the 
  443.   optional arguments are given.  Without any of them, it returns the entire
  444.   alist.  With <slot>, will return the result of CDR-ASSOCing the given <slot> 
  445.   into this list, that is: ((<key> . <info>)*).  With <slot> and <keyword>
  446.   specified, returns the result of  CDR-ASSOCing <keyword> into the result of
  447.   CDR-ASSOCing <slot>: <info>."
  448.   (cond ((and slot keyword)
  449.          `(cdr (assoc ,keyword
  450.                       (cdr (assoc ,slot
  451.                                   (structure-type-slot-info
  452.                                    (the structure-type 
  453.                                         (get ,type '$structure-type$))))))))
  454.         (slot
  455.          `(cdr (assoc ,slot
  456.                       (structure-type-slot-info
  457.                        (the structure-type (get ,type '$structure-type$))))))
  458.         (T
  459.          `(structure-type-slot-info
  460.            (the structure-type (get ,type '$structure-type$))))))
  461.  
  462. (defmacro SLOT-TYPES (type)
  463.   "slot-types <type>                                                   [Macro]
  464.   Returns a-list of (<slot-name> . <slot-type>) pairs for <type>."
  465.   `(structure-type-slot-types
  466.     (the structure-type (get ,type '$structure-type$))))
  467.  
  468. (defmacro TYPE-INFO (type &optional keyword)
  469.   "type-info <type> &optional keyword                                  [Macro]
  470.   Access to an association list of keywords to information for the type,
  471.   which looks like: ((<key-1> . <info>) ... (<key-N> . <info>)), where
  472.   <key> is a user-supplied type option keyword, and <info> is whatever
  473.   DST found after it.  The expansion depends on whether the optional 
  474.   argument is given.  Without the optional argument, it returns the 
  475.   entire alist.  With <keyword>, will return the result of CDR-ASSOCing 
  476.   the given <keyword> into this list, that is: <info>.  Setf works."
  477.   (if keyword 
  478.     `(cdr (assoc ,keyword
  479.                  (structure-type-info 
  480.                   (the structure-type (get ,type '$structure-type$)))))
  481.     `(structure-type-info 
  482.       (the structure-type (get ,type '$structure-type$)))))
  483.  
  484. (defmacro UNCOMPUTED-SLOTS (type)
  485.   "uncomputed-slots <type>                                             [Macro]
  486.   Returns list of slot names which are not computed for <type>.  This
  487.   is in the same order as the arguments to the function create-<type>."
  488.   `(structure-type-uncomputed-slots
  489.     (the structure-type (get ,type '$structure-type$))))
  490.  
  491. ;;;--------------------------------------------------------
  492. ;;; Instance access.  For efficiency, we'll have to trust them on the setf.
  493.  
  494. (defmacro GETS (type name)
  495.   "gets <type> <name>                                                  [Macro]
  496.   Retrieves the representation an instance of <type> called <name>.
  497.   Defstruct functions may be used on the result.  Do not SETF this!"
  498.   `(get (the symbol ,name) (the symbol ,type)))
  499.  
  500. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  501. ;;;
  502. ;;;                         INSTANCE FUNCTIONS
  503. ;;;
  504. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  505.  
  506. (defun INSTANCES (type) ; a function to prevent user setfs
  507.   "instances <type>                                                 [Function]
  508.   Returns a list of all the names of struct instances of <type>."
  509.   (check-type type symbol)
  510.   (assert (get type '$structure-type$) (type) "Type is not known.")
  511.   (instance-names type))
  512. (proclaim '(function instances (symbol) list))
  513.  
  514. ;;; Copying is fraught with difficulties: Common Lisp provides no generic 
  515. ;;; copy function; attempting to copy a circular object will result in an 
  516. ;;; infinite loop or stack overflow; and some objects have no copy function.  
  517. ;;; The notion of a 'copy' is ill defined. Should sublists be copied? If so, 
  518. ;;; how are circularities preserved? Should copy-symbol be used?  
  519. ;;; For these reasons, I don't attempt to do more than copy-tree of conses.
  520.  
  521. (defun COPIES (type source target &key (copy-tree t))
  522.   "copies <type> <source> <target> &key (copy-tree t)               [Function]
  523.   Creates a copy of instance <source> whose name is <target>.  If <target>
  524.   already exists, it will be destroyed first.  Returns <target>. If 
  525.   :copy-tree is T (the default), any slot value which is a cons is copied 
  526.   with copy-tree. Otherwise, the slot values of the copied instance are the
  527.   same memory objects as those of the original.  To attempt to guarantee 
  528.   that memory is not shared, evaluate the macro representation of the 
  529.   instance and recompute its computed slots."
  530.   (check-type type   symbol)
  531.   (check-type source symbol)
  532.   (check-type target symbol)
  533.   (assert (gets type source) (type source) "Unknown type or source instance name.")
  534.   (let ((slots->access (slot-access type))
  535.     (source-struct (gets type source)))
  536.     (declare (list slots->access) (optimize (safety 1) (space 2) (speed 3)))
  537.     (when (gets type target)
  538.       (if *warn-of-redefinitions*
  539.       (warn "~%[SM:COPYS] ~S of type ~S being redefined to be a copy of ~S"
  540.         target type source))
  541.       (destroys type target))
  542.     ;; Create the instance, with correct :uncomputed slots (including :read-only)
  543.     (apply (creator type)
  544.        target
  545.        (mapcar #'(lambda (uslot)
  546.                (declare (symbol uslot))
  547.                        (let ((val (funcall (cdr (assoc uslot slots->access))
  548.                                              source-struct)))
  549.                            (if (and copy-tree (consp val)) (copy-tree val) val)))
  550.            (uncomputed-slots type)))
  551.     ;; Copy :computed slots in.
  552.     (dolist (cslot (computed-slots type))
  553.       (eval `(setf (,(cdr (assoc cslot slots->access)) ',(gets type target))
  554.                    (let ((val (,(cdr (assoc cslot slots->access)) ',source-struct)))
  555.                      (if (and ',copy-tree (consp val)) (copy-tree val) val)))))
  556.     target))
  557.  
  558. ;;; In the process of unassociating the struct with the <name>, this
  559. ;;; function saves the struct on a free list so it may be reused to
  560. ;;; create future instances.  Use DESTROY-TYPE to free up space for GC.
  561.   
  562. (defun DESTROYS (type name)
  563.   "destroys <type> <name>                                           [Function]
  564.   Destroys the <name> struct instance of <type>.  If (reusable type), 
  565.   instance memory is saved for reuse when new instances are allocated.
  566.   Use FLUSH-FREELIST or DESTROY-TYPE to reclaim the freelist."
  567.   (check-type type symbol)
  568.   (check-type name symbol)
  569.   (assert (gets type name) (type name) "Unknown type or instance name.")
  570.   (setf (instance-names type)
  571.         (delete name (the list (instance-names type))))
  572.   ;; Save only if reusable, and this thing was not clobbered already.
  573.   (if (and (reusable type) (get name type))
  574.       (push (get name type) (freelist type)))
  575.   (remprop name type)
  576.   name)
  577. (proclaim '(function destroys (symbol symbol) symbol))
  578.  
  579. (defun PRINTS (type name &key (stream *standard-output*) 
  580.                     (style :brief) (omit nil))
  581.   "prints <type> <name> &key :stream :style :omit                   [Function]
  582.   Prints a <type> instance called <name>.  Keyword arg :stream defaults to
  583.   T.  The :style is one of:
  584.     :name         -- prints <<type> <name>>
  585.     :brief        -- prints uncomputed slots with length and depth cutoff, ~A
  586.     :summary      -- prints all slots, less stringent cutoff than :brief, uses ~S
  587.     :pretty       -- all slots printed, no cutoff, *print-pretty* t, ~A
  588.     :macro        -- re-readable form (no uncomputed slots), one slot per line
  589.     :pretty-macro -- like :macro but pretty printed (multiple lines per slot)
  590.     :list-macro   -- like :macro but a list rather than string is returned,
  591.                      and no printing is done (<stream> is ignored).
  592.   Default is :brief.  Pretty forms print slowly.  Argument :omit is a list of 
  593.   slot names; these slots are not printed."
  594.   (check-type type   symbol)
  595.   (check-type name   symbol)
  596.   (check-type style  (member :name :brief :summary :pretty 
  597.                              :macro :pretty-macro :list-macro))
  598.   (check-type stream (or null simple-string stream))
  599.   (check-type omit   list)
  600.   (assert (gets type name) (type name) "Unknown type or instance name.")
  601.   (let* ((struct (gets type name))
  602.          (newlinestring (format nil "~% "))
  603.          (newline (char newlinestring 0)))
  604.     (declare (type structure-type struct) 
  605.              (simple-string newlinestring) 
  606.              (character newline))
  607.     (case style
  608.       ((:name)
  609.        (let ((*print-case* :capitalize))
  610.          (format stream "<~A ~A>" type name)))
  611.       ((:brief)
  612.        (let ((*print-pretty* t) (*print-escape* nil) (*print-circle* t) 
  613.              (*print-case* :capitalize) (*print-array* nil)
  614.              (*print-level* 2) (*print-length* 3)
  615.              #+:ccl (ccl::*print-structure* nil)
  616.              )
  617.          ;; Speed optimized in here since we are past the crucial argument
  618.          ;; checks, and the printer is slow.
  619.          (declare (optimize (safety 1) (space 2) (speed 3)))
  620.          (format stream "[~A ~A:~{~& ~A: ~(~S~)~}]" type name
  621.                  (mapcan #'(lambda (slot+access)
  622.                              (declare (cons slot+access))
  623.                              (unless (member (car slot+access) omit)
  624.                                (list (car slot+access)
  625.                                      (funcall (cdr slot+access) struct))))
  626.                          (the list (macro-access type))))))
  627.       ((:summary)
  628.        (let ((*print-pretty* t) (*print-escape* nil) (*print-circle* t) 
  629.              (*print-case* :capitalize) (*print-array* nil)
  630.              (*print-level* 3) (*print-length* 10)
  631.              #+:ccl (ccl::*print-structure* nil)
  632.              )
  633.          (declare (optimize (safety 1) (space 2) (speed 3)))
  634.          (format stream "[~S ~S:~{~& ~A: ~15,5T~(~S~)~}]" type name
  635.                  (mapcan #'(lambda (slot+access)
  636.                              (declare (cons slot+access))
  637.                              (unless (member (car slot+access) omit)
  638.                                (list (car slot+access)
  639.                                      (funcall (cdr slot+access) struct))))
  640.                          (the list (slot-access type))))))
  641.       ((:pretty)
  642.        (let ((*print-pretty* t) (*print-escape* nil) (*print-circle* t) 
  643.              (*print-case* :capitalize) (*print-array* nil)
  644.              #+:ccl (ccl::*print-structure* nil)
  645.              )
  646.          (declare (optimize (safety 1) (space 2) (speed 3)))
  647.          (format stream "[~S ~S:~{~& ~A: ~15,5T~(~A~)~}]" type name
  648.                  (mapcan #'(lambda (slot+access)
  649.                              (declare (cons slot+access))
  650.                              (unless (member (car slot+access) omit)
  651.                                (list (car slot+access)
  652.                                      ;; Allegro CL did not pretty print format.
  653.                                      ;; Also want newline if it is a big object.
  654.                                      (let* ((*print-case* :downcase)
  655.                                             (s (princ-to-string
  656.                                                 (funcall (cdr slot+access) struct))))
  657.                                        (declare (string s))
  658.                                        (if (find newline s)
  659.                                          (concatenate 'string newlinestring s)
  660.                                          s)))))
  661.                          (the list (slot-access type))))))
  662.       ((:macro)
  663.        (let ((*print-pretty* nil) (*print-escape* t) (*print-circle* nil) 
  664.              (*print-case* :upcase) (*print-array* t) 
  665.              #+:ccl (ccl::*print-structure* t)
  666.              )
  667.          (declare (optimize (safety 1) (space 2) (speed 3)))
  668.          (format stream "(~S ~S~{~& :~A ~S~})" type name
  669.                  (mapcan #'(lambda (slot+access)
  670.                              (declare (cons slot+access))
  671.                              (unless (member (car slot+access) omit)
  672.                                (list (car slot+access)
  673.                                      (funcall (cdr slot+access) struct))))
  674.                          (the list (macro-access type))))))
  675.       ((:pretty-macro) 
  676.        (let ((*print-pretty* t) (*print-escape* t) (*print-circle* nil) 
  677.              (*print-case* :upcase) (*print-array* t) 
  678.              #+:ccl (ccl::*print-structure* t)
  679.              )
  680.          (declare (optimize (safety 1) (space 2) (speed 3)))
  681.          (format stream "(~S ~S~{~& :~A ~20,5T~A~})" type name
  682.                  (mapcan #'(lambda (slot+access)
  683.                              (declare (cons slot+access))
  684.                              (unless (member (car slot+access) omit)
  685.                                (let* ((*print-case* :downcase)
  686.                                       (s (prin1-to-string
  687.                                           (funcall (cdr slot+access) struct))))
  688.                                  (declare (string s))
  689.                                  (list (car slot+access)
  690.                                        (if (find newline s)
  691.                                          (concatenate  'string newlinestring s)
  692.                                          s)))))
  693.                          (the list (macro-access type))))))
  694.       ((:list-macro)
  695.        `(,type ,name 
  696.                ,.(mapcan #'(lambda (slot+access)
  697.                              (declare (cons slot+access))
  698.                              (unless (member (car slot+access) omit)
  699.                                (list (intern (symbol-name (car slot+access))
  700.                                              *keyword-package*)
  701.                                      (funcall (cdr slot+access) struct))))
  702.                          (the list (macro-access type))))))))
  703. (proclaim '(function prints (symbol symbol &key stream keyword list) t))
  704.  
  705. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  706. ;;;
  707. ;;;                          TYPE FUNCTIONS
  708. ;;;
  709. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  710.  
  711. (defun STRUCTURE-TYPES () ; This remains a function to prevent user setfs.
  712.  "structure-types                                                   [Function]
  713.   Returns a list of defined structure types."
  714.   *structure-types*)
  715. (proclaim '(function structure-types () list))
  716.  
  717. (defun RESET-TYPE (type)
  718.   "reset-type <type>                                                [Function]
  719.   Destroys all instances of <type>.  If (reusable type), instance
  720.   memory is saved for reuse when new instances are allocated."
  721.   (check-type type symbol)
  722.   (assert (get type '$structure-type$) (type) "Type is not known.")
  723.   (if (reusable type)
  724.       (dolist (name (instance-names type))
  725.         (declare (symbol name))
  726.         (if (get name type)
  727.             (push (get name type) (freelist type)))
  728.         (remprop name type))
  729.       (dolist (name (instance-names type))
  730.         (declare (symbol name))
  731.         (remprop name type)))
  732.   (setf (instance-names type) nil)
  733.   type)
  734. (proclaim '(function reset-type (symbol) symbol))
  735.  
  736. ;;; This function removes all references to the storage taken up by
  737. ;;; instances within SM, so if no other references exists, the space
  738. ;;; will be reclaimed by GC.
  739.  
  740. (defun FLUSH-FREELIST (type)
  741.   "flush-freelist <type>                                            [Function]
  742.   Empties the freelist of <type>, making that storage available for
  743.   garbage collection."
  744.   (check-type type symbol)
  745.   (assert (get type '$structure-type$) (type) "Type is not known.")
  746.   (setf (freelist type) nil))
  747. (proclaim '(function flush-freelist (symbol) null))
  748.  
  749. (defun DESTROY-TYPE (type)
  750.   "destroy-type <type>                                              [Function]
  751.   Destroys all instances of <type>, and then undefines the <type>."
  752.   (check-type type symbol)
  753.   (assert (get type '$structure-type$) (type) "Type is not known.")
  754.   (dolist (instance (instance-names type))
  755.     (declare (symbol instance))
  756.     (remprop instance type))
  757.   ;; About to mess with the type definition, so make it unofficial.
  758.   (setf *structure-types* (delete type *structure-types*))
  759.   ;; Undefine the functions.  This helps redefinitions behave right.
  760.   (fmakunbound type)
  761.   (fmakunbound (creator type))
  762.   (dolist (slot+access (slot-access type))
  763.     (fmakunbound (cdr slot+access)))
  764.   ;; This has to be last since we access property in getting above info.
  765.   (remprop type '$structure-type$)
  766.   type)
  767. (proclaim '(function destroy-type (symbol) symbol))
  768.  
  769. (defun RESET-ALL-TYPES ()
  770.   "reset-all-types                                                  [Function]
  771.   Destroys all instances in SM, but leaves type definitions intact."
  772.   (dolist (type *structure-types*) 
  773.     (declare (symbol type))
  774.     (reset-type type)))
  775. (proclaim '(function reset-all-types () null))
  776.  
  777. (defun DESTROY-ALL-TYPES ()
  778.   "destroy-all-types                                                [Function]
  779.   DESTROYS all instances, and undefines all types, leaving SM in the state
  780.   it was in when intitially loaded (though not common lisp)."
  781.   (dolist (type (copy-tree *structure-types*)) ; list is modified below.
  782.     (declare (symbol type))
  783.     (destroy-type type)))
  784. (proclaim '(function destroy-all-types () null))
  785.  
  786. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  787. ;;;
  788. ;;;                              TYPE DEFINITION
  789. ;;;
  790. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  791. ;;; First we start with some helpers for defining DST and DEFINE-TYPE.
  792. ;;; These use numerous symbols in the lexical environment the expansion sees.
  793.  
  794. (eval-when (compile eval)
  795.  
  796.   (defmacro MAKE-NAMES-OF-THINGS ()
  797.     '(progn
  798.        (if (atom type-and-options)
  799.          (setf type-and-options (list type-and-options))) ; For uniformity 
  800.        (setq type        (first type-and-options))
  801.        (setq type-string (symbol-name type))
  802.        (setq creator (intern (concatenate 'string "CREATE-" type-string)
  803.                              *package*))
  804.        (setq maker   (intern (concatenate 'string "SM$ALLOCATE-" type-string)
  805.                              *package*)))) ; because if in SM, may collide!
  806.  
  807.   (defmacro PROCESS-SLOT-DEFINITIONS (calling-function-name)
  808.     ;; Loop to process the slot-definitions, constructing defstruct canonical form
  809.     ;; and recording slot definition information.
  810.     `(do ((sdptr slot-definitions (rest sdptr))
  811.           (sdef nil) (sname nil) (sdefault) (stype nil) (sread-only nil) 
  812.           (saccess nil) (scomputed nil) (sinfo nil))
  813.          ((null sdptr)
  814.           (progn
  815.             ;; Knock off :heads.
  816.             (pop read-only-slots)
  817.             (pop computed-slots)
  818.             (pop uncomputed-slots)
  819.             (pop defstruct-slot-definitions)
  820.             (pop slot-defaults)
  821.             (pop slot-info)
  822.             (pop slot-types)
  823.             (pop slot-access)
  824.             (pop macro-access)))
  825.        (declare (list sdptr sdef sinfo) 
  826.                 (symbol sname saccess sread-only scomputed))
  827.        
  828.        ;; Change atomic slot definitions to (<slot> nil) for uniformity.
  829.        (setq sdef
  830.              (if (atom (first sdptr)) (list (first sdptr) nil) (first sdptr)))
  831.        
  832.        ;; Get or create all standard slot information. 
  833.        ;; Create <type>-<slot> slot access symbol in calling package.
  834.        (setf sname      (first sdef))
  835.        (setf sdefault   (second sdef))
  836.        (setf stype      (or (second (member :type sdef)) T))
  837.        (setf sread-only (second (member :read-only sdef)))
  838.        (setf scomputed  (second (member :computed  sdef)))
  839.        (setf saccess    (intern (concatenate 'string type-string "-"
  840.                                              (symbol-name sname))
  841.                                 *package*))
  842.        
  843.        ;; Record defstruct version of the slot definition.
  844.        (nconc defstruct-slot-definitions 
  845.               (list (list sname sdefault :type stype :read-only sread-only)))
  846.        
  847.        ;; Record the slot's information on appropriate association lists.
  848.        (nconc slot-defaults (list (cons sname sdefault)))
  849.        (nconc slot-types    (list (cons sname stype)))
  850.        (nconc slot-access   (list (cons sname saccess)))
  851.        (when sread-only
  852.          (if scomputed
  853.            (error ,(concatenate 'string 
  854.                                 "[SM:"
  855.                                 calling-function-name
  856.                                 "] ~S Computed slot cannot be read only: ~S")
  857.                   type sdef))
  858.          (setf reusable nil)
  859.          (nconc read-only-slots (list sname)))
  860.        (if scomputed
  861.          (nconc computed-slots (list sname))
  862.          (progn
  863.            ;; Subset of slot-access; used to drive printer.
  864.            (nconc macro-access (list (cons sname saccess)))
  865.            ;; Retain order to be same as create-<type> args.
  866.            (nconc uncomputed-slots (list sname))))
  867.        
  868.        ;; Find and record all non-standard slot options by searching from
  869.        ;; after the name and default.
  870.        (setf sinfo (cons sname nil))
  871.        (do ((opt-ptr (cddr sdef) (cddr opt-ptr)))
  872.            ((null opt-ptr))
  873.          (declare (list opt-ptr))
  874.          (if (not (member (first opt-ptr) '(:type :read-only :computed)))
  875.            (push (cons (first opt-ptr) (second opt-ptr))
  876.                  (cdr sinfo))))
  877.        (nconc slot-info (list sinfo))))
  878.  
  879.   (defmacro PROCESS-TYPE-DEFINITION (calling-function-name)
  880.     `(flet ((find-spec (key specs)
  881.                        (declare (keyword key) (list specs))
  882.                        (if (eq key :named)
  883.                          (if (member :named (cdr specs)) :named)
  884.                          (first (member key (cdr specs)
  885.                                         :key #'(lambda (el)
  886.                                                  (if (listp el) (first el) nil)))))))
  887.        ;; Get all standard type information.  
  888.        ;; Need to distinguish a given NIL from a non-given spec.
  889.        (let ((comments-spec       (find-spec :comments       type-and-options))
  890.              (initial-offset-spec (find-spec :initial-offset type-and-options))
  891.              (named-spec          (find-spec :named          type-and-options))
  892.              (predicate-spec      (find-spec :predicate      type-and-options))
  893.              (print-function-spec (find-spec :print-function type-and-options))
  894.              (reusable-spec       (find-spec :reusable       type-and-options))
  895.              (redefine-spec       (find-spec :redefine       type-and-options))
  896.              (sort-instances-spec (find-spec :sort-instances type-and-options))
  897.              (type-spec           (find-spec :type           type-and-options)))
  898.          (declare (list comments-spec initial-offset-spec predicate-spec 
  899.                         print-function-spec reusable-spec redefine-spec 
  900.                         sort-instances-spec type-spec)
  901.                   (symbol named-spec))
  902.          
  903.          ;; Check for consistency of :reusable specification with slot :read-only.
  904.          ;; If consistent and they asked for reusable, honor it.
  905.          (cond ((and reusable-spec (second reusable-spec) (not reusable))
  906.                 (error 
  907.                  ,(concatenate 'string 
  908.                                "[SM:"
  909.                                calling-function-name
  910.                                "] ~S :reusable T incompatible with :read-only slot")
  911.                  type))
  912.                ((and reusable-spec (not (second reusable-spec)))
  913.                 (setf reusable nil)))
  914.          
  915.          ;; Record comments, redefine and sort-instances.
  916.          (if comments-spec       (setf documentation  (second comments-spec)))
  917.          (if redefine-spec       (setf redefine       (second redefine-spec)))
  918.          (if sort-instances-spec (setf sort-instances (second sort-instances-spec)))
  919.          
  920.          ;; Check consistency of and record representation specifications.
  921.          (cond  (type-spec
  922.                  (setf representation (second type-spec))
  923.                  (if initial-offset-spec 
  924.                    (setf initial-offset (second initial-offset-spec)))
  925.                  (if named-spec (setf named t) (setf named nil)))
  926.                 ((or initial-offset-spec named-spec)
  927.                  (error 
  928.                   ,(concatenate 'string 
  929.                                 "[SM:"
  930.                                 calling-function-name
  931.                                 "] ~S :initial-offset and :named cannot be given without :type.")
  932.                   type)))
  933.          
  934.          ;; Construct the defstruct version, using permitted options if asked for.
  935.          ;; Add :constructor option for faster BOA constructor (see p. 315 CLtL).
  936.          ;; Keywords not needed since user never calls allocate-<type>.  Defaults 
  937.          ;; not needed since create-<type> will supply them.
  938.          (setf defstruct-type-and-options
  939.                `(,type 
  940.                  ,(list :constructor maker
  941.                         (mapcar #'car defstruct-slot-definitions))
  942.                  ,.(if predicate-spec      (list predicate-spec))
  943.                  ,.(if print-function-spec (list print-function-spec))
  944.                  ,.(if type-spec           (list type-spec))
  945.                  ,.(if named-spec          (list named-spec))
  946.                  ,.(if initial-offset-spec (list initial-offset-spec))))
  947.          
  948.          ;; Find and record user extensions to type options.
  949.          (do ((opt-ptr (cdr type-and-options) (cdr opt-ptr)))
  950.              ((null opt-ptr))
  951.            (declare (list opt-ptr))
  952.            (cond ((atom (car opt-ptr))
  953.                   (if (not (eq (car opt-ptr) :named))
  954.                     (error 
  955.                      ,(concatenate 'string 
  956.                                    "[SM:"
  957.                                    calling-function-name
  958.                                    "] ~S has bad type option ~S.")
  959.                      type (car opt-ptr))))
  960.                  ((not (member (caar opt-ptr)
  961.                                '(:reusable :redefine :sort-instances :predicate 
  962.                                  :print-function :type :initial-offset)))
  963.                   (push (cons (caar opt-ptr) (cadar opt-ptr)) type-info)))))))
  964.  
  965.   ) ; end of eval-when
  966.  
  967. (defvar *SAVED-INSTANCES* nil
  968.   "DEFINE-TYPE saves macro representations of all instances of a type being
  969.   redefined on this global before destroying the type.  While this is normally
  970.   for internal use only, it is exported in case there is an error during the 
  971.   redefinition, and the user wishes to recover the instances.")
  972. (proclaim '(list *saved-instances*))
  973.  
  974. (defun SAVE-INSTANCES (type new-computed-slots new-uncomputed-slots) 
  975.   ;; Save on *saved-instances* evaluatable macro representations of instances
  976.   ;; using the print function on the old type.
  977.   (declare (symbol type) (special *saved-instances*))
  978.   (dolist (r (instances type))
  979.     (declare (symbol r))
  980.     (push (prints type r
  981.                   :style :list-macro 
  982.                   :omit
  983.                   ;; Omit all slots computed in the new type, and all slots
  984.                   ;; which were uncomputed in the old but are being deleted.
  985.                   (union new-computed-slots
  986.                          (set-difference (uncomputed-slots type)
  987.                                          (union new-computed-slots
  988.                                                 new-uncomputed-slots))))
  989.           *saved-instances*))
  990.   
  991.   ;; Slots which are now computed but will be uncomputed can have their values
  992.   ;; defined in the new instances.  Need to add explicitly since prints of the
  993.   ;; old type won't get them there.
  994.   (dolist (slot (intersection (computed-slots type) new-uncomputed-slots))
  995.     (declare (symbol slot))
  996.     (dolist (irep *saved-instances*)
  997.       (declare (list irep))
  998.       (nconc irep (list (intern (symbol-name slot) *keyword-package*)
  999.                         (funcall (cdr (assoc slot (slot-access type)))
  1000.                                  (gets type (second irep)))))))
  1001.   type)
  1002.  
  1003. (defun RESTORE-INSTANCES ()
  1004.   ;; There will be a lot of redefinitions, and we already warned if it was T.
  1005.   (let ((*warn-of-redefinitions* nil))
  1006.     (loop
  1007.       (if (null *saved-instances*) (return))
  1008.       (eval (pop *saved-instances*)))))
  1009.  
  1010. (defmacro INSERT-IN-SORTED-LIST (element list-place)
  1011.   `(let ((element-name (symbol-name ,element)))
  1012.      (declare (string element-name) (optimize (safety 1) (space 2) (speed 3)))
  1013.      (cond ((null ,list-place) 
  1014.             (setf ,list-place (list ,element)))
  1015.            ((string< element-name (symbol-name (first ,list-place)))
  1016.             (push ,element ,list-place))
  1017.            (T
  1018.             ;; Invariant: (first lptr) always < element.  Looking to insert after.
  1019.             (do ((lptr ,list-place (cdr lptr))
  1020.                  (successp nil))
  1021.                 ((or successp (null (cdr lptr)))
  1022.                  (if (not successp) 
  1023.                    (nconc ,list-place (list ,element))
  1024.                    ,list-place))
  1025.               (declare (list lptr))
  1026.               (when (string< element-name (symbol-name (second lptr)))
  1027.                 (setf (cdr lptr) (cons ,element (cdr lptr)))
  1028.                 (setf successp t)))))))
  1029.  
  1030. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1031. ;;; This macro expands into a single progn containing all needed definitions.
  1032. ;;; (The function and macro definitions in a progn are compiled.)  This is
  1033. ;;; truly a "hairy macro". It works in two parts: cooking the arguments,
  1034. ;;; and generating the expansion.  You are advised to use macroexpand to 
  1035. ;;; thoroughly understand this macro before attempting to change it.
  1036.  
  1037. (defmacro DST (&whole defining-form
  1038.                       type-and-options
  1039.                       &rest slot-definitions
  1040.                       &aux 
  1041.                            defstruct-type-and-options  ; Defstruct versions
  1042.                            (defstruct-slot-definitions ;   (sans extra specs). 
  1043.                             (list :head))              ;
  1044.                            type                        ; symbol name of type
  1045.                            (type-string "")            ; string name of type
  1046.                            (documentation nil)         ; from :comments spec
  1047.                            (reusable T)                ; will update this
  1048.                            (redefine nil)              ; 
  1049.                            (sort-instances nil)        ; whether sort code generated.
  1050.                            (initial-offset 0)          ; type options
  1051.                            (representation nil)
  1052.                            (named T)
  1053.                            (type-info nil)             ; ((key . info)*)
  1054.                            ;; :Head for nconc to preserve order user gave, in some
  1055.                            ;; cases so create-<type> has right argument order, but
  1056.                            ;; for alists it is for efficiency since presumably the
  1057.                            ;; most used slots are given first.
  1058.                            (slot-access      (list :head)) ; ((name . access)*)
  1059.                            (macro-access     (list :head)) ; ditto, uncomputed only
  1060.                            (read-only-slots  (list :head))
  1061.                            (computed-slots   (list :head))
  1062.                            (uncomputed-slots (list :head))
  1063.                            (slot-defaults (list :head)); ((name . default)*)
  1064.                            (slot-types (list :head))   ; ((name . type)*)
  1065.                            (slot-info (list :head))    ; ((name . ((key . info)*))*)
  1066.                            creator                     ; create-<type>
  1067.                            maker                       ; make-<type>
  1068.                            )
  1069.  
  1070.   "DST <type-and-options>  <slot-spec>*                                [Macro]
  1071.  
  1072.   Defines a common lisp structure type, and supporting Structure Manager
  1073.   code.  Syntax is nearly identical to that of defstruct, except as noted:
  1074.  
  1075.   <type-and-options> ::= <type>    |  ( <type> <type-option>* )
  1076.   <type>             ::= <symbol>
  1077.   <type-option>      ::= :named    |  ( <keyword> <argument> )
  1078.   <slot-spec>        ::= <symbol>  |  ( <symbol> <default> <slot-option>* )
  1079.   <slot-option>      ::= <keyword> <argument>
  1080.  
  1081.   This macro will:
  1082.     1. Check for existing definition of <type> and destroy or redefine
  1083.        it as directed by the :redefine type option (described below);
  1084.     2. Define the common lisp structure for instances of <type>;
  1085.     3. Record the new type definition in SM data structures;
  1086.     4. Define create-<type> function to create instances; and
  1087.     5. Define <type> macro for no-eval creation of instances.
  1088.   All symbols defined by DST are defined in the calling package.
  1089.  
  1090.   Notes on Type Options:
  1091.  
  1092.     :CONC-NAME      - NOT allowed. Code assumes default in various places.
  1093.     :CONSTRUCTOR    - NOT allowed.  Use create-<type>.
  1094.     :COPIER         - NOT allowed.  Use COPIES.
  1095.     :INCLUDE        - NOT allowed. Possible but would take a major revision.
  1096.     :PREDICATE      - OK to use option.  You can apply it to result of GETS.
  1097.       However, in most cases, (gets 'type name) suffices as a predicate.
  1098.     :PRINT-FUNCTION - OK to use (SM doesn't use this).  Will only affect
  1099.       printing of structures in things like error messages and traces.
  1100.     :TYPE, :NAMED, :INITIAL-OFFSET - Probably OK to use.
  1101.  
  1102.     :COMMENTS - String; will be recorded as the type's documentation.
  1103.  
  1104.     :REUSABLE - A new type option.  When T, storage for structures is to
  1105.       be reused. Default T unless a slot is :read-only. If ANY slot has 
  1106.       :read-only T, it is an error for :reusable to be T (as the slot 
  1107.       cannot be reassigned for new instances).
  1108.  
  1109.     :REDEFINE - Syntactically, a new type option, but semantically this
  1110.       option is associated solely with the particular invocation of DST,
  1111.       and indicates whether that invocation should redefine or destroy
  1112.       an existing type of the same name, if found. When T, defining an 
  1113.       existing type results in redefinition of the type, with any existing
  1114.       instances re-represented as instances of the new type.  Existing 
  1115.       instances will be converted to the new representation if possible.  
  1116.       It is always possible to reorder, add, or delete slots, and to extend 
  1117.       the range of their types.  Restricting their types is only possible 
  1118.       if all of the existing instances meet the new type restriction.  New 
  1119.       slots will be initialized to default values. When NIL (default), 
  1120.       redefinition will result in destruction of the existing type and 
  1121.       instances first. :Redefine makes the most sense with DEFINE-TYPE.
  1122.       Interacts with *warn-of-redefinitions* as follows:
  1123.  
  1124.                    | Redefine T        | Redefine nil
  1125.       -------------|-------------------|-------------------------------------
  1126.       Warnings off | Redefine silently | Destroy silently
  1127.       -------------|-------------------|-------------------------------------
  1128.       Warnings on  | Redefine and Warn | If instances exist, Cerror continued
  1129.                    |                   | with Destroy; Else Destroy and Warn
  1130.       -------------|-------------------|-------------------------------------
  1131.  
  1132.     :SORT-INSTANCES - A new type option.  Default NIL: If T, the instance
  1133.       creation function will ensure that (instances '<type>) always returns
  1134.       a list sorted by symbol name.  Slows down instance creation.
  1135.  
  1136.     :<ANYTHING> - Type options may be extended arbitrarily by the user.
  1137.       Any keyword-argument list whose keyword is not one of the above will
  1138.       be added to the association list accessible in TYPE-INFO.  For example:
  1139.         (dst (MY-TYPE (:reusable T) (:specializes more-general-type)) ...)
  1140.       results in a TYPE-INFO of ((:specializes more-general-type)).
  1141.  
  1142.   Notes on Slot Options:
  1143.  
  1144.     :READ-ONLY - OK to use.  See :reusable.  Incompatible with :computed.
  1145.     :TYPE      - OK to use, and I highly recommend it (see SLOT-TYPES).
  1146.  
  1147.     :COMPUTED - A new slot option.  When T, SM assumes the contents of 
  1148.       the slot are always computed at run time.  Therefore, the slot is 
  1149.       not printed in macro representations of instances, and there is no
  1150.       corresponding argument to create-<type> or <type> macro.  Default nil.
  1151.  
  1152.     :<ANYTHING> - Slot options may be extended arbitrarily by the user.
  1153.       Any keyword-argument pair whose keyword is not one of the above slot
  1154.       options will be recorded.  For example:
  1155.         (my-slot nil :type list :computed t :if-needed (lambda () ...))
  1156.       SLOT-INFO returns an association list of slot names to nested alists
  1157.       of keywords to arguments.  For example, slot-info will have:
  1158.         (... (my-slot . (:if-needed . (lambda () ...))) ...)
  1159.  
  1160.   About slot-defaults:
  1161.  
  1162.   For various technical reasons, the <default> expressions are NOT evaluated until
  1163.   the time at which a default is needed.  For this reason, these expressions should
  1164.   not incur side effects.  Nor should they depend on the current environment (unless
  1165.   you wish to change the default as a function of environment).
  1166.  
  1167.   To be safe, only use DST at top level.  Use the function DEFINE-TYPE for 
  1168.   non-top-level definitions."
  1169.  
  1170.   (declare (symbol type reusable redefine creator maker)
  1171.            (simple-string type-string)
  1172.            (list defining-form slot-definitions defstruct-type-and-options 
  1173.                  defstruct-slot-definitions type-info slot-access macro-access 
  1174.                  read-only-slots computed-slots uncomputed-slots 
  1175.                  slot-defaults slot-info slot-types))
  1176.  
  1177.   ;; ------------------------------------------------------------------------
  1178.   ;; The first section sets up various lists used to construct the expansion,
  1179.   ;; and saved in the structure-type structure.  None of this depends on the
  1180.   ;; run time environment (it can be done at compile time). 
  1181.   ;; ------------------------------------------------------------------------
  1182.  
  1183.   (make-names-of-things)
  1184.   (process-slot-definitions "DST")
  1185.   (process-type-definition  "DST")
  1186.  
  1187.   ;; ------------------------------------------------------------------------
  1188.   ;; The expanded form begins with progn, to make all definitions top level.
  1189.   ;; ------------------------------------------------------------------------
  1190.  
  1191.   `(progn
  1192.  
  1193.      ;; If this is a redefinition, make sure all the instances of the type
  1194.      ;; are disposed or saved, checking with or warning user as as appropriate.
  1195.      ;; Why important: Create-<type> checks for redefinition of an instance 
  1196.      ;; by testing the <type> property of the instance name, instead of the
  1197.      ;; slower member test of instance names.  If the instance exists, its
  1198.      ;; name is not added to the name list.  Thus, if these properties are
  1199.      ;; not reset upon type redefinition, instances which are not on the 
  1200.      ;; instance name list will appear to exist, and will erroneously not be
  1201.      ;; placed on the instance list.  A destroy-type call avoids this.
  1202.  
  1203.      (when (member ',type *structure-types*)
  1204.        ,.(if redefine
  1205.           (list 
  1206.            (list 'save-instances
  1207.                  (list 'quote type)              
  1208.                  (list 'quote computed-slots)    ; Save-instances can access the
  1209.                  (list 'quote uncomputed-slots)) ; existing counterparts of these.
  1210.            (list 'when '*warn-of-redefinitions*
  1211.                  (list 'warn
  1212.                        (list 'format nil
  1213.                              "~%[SM:DST] ~S being redefined. ~A"
  1214.                              (list 'quote type)
  1215.                              (list 'if (list 'instances (list 'quote type))
  1216.                                    "Instances temporarily saved on
  1217. *saved-instances*, and will be redefined as instances of the new type."
  1218.                                    "(There were no instances.)"))))
  1219.            (list 'destroy-type (list 'quote type)))
  1220.           (list
  1221.            (list 'if '*warn-of-redefinitions*
  1222.                  (list 'if (list 'instances (list 'quote type))
  1223.                        ;; User may want to save in-memory instances first.
  1224.                        (list 'cerror
  1225.                              "Will proceed, calling DESTROY-TYPE first."
  1226.                              (concatenate 
  1227.                               'string 
  1228.                               "~%[SM:DST] Type " (symbol-name type)
  1229.                               " is defined, and has in-memory instances which~
  1230.                                ~%would be destroyed by redefinition."))
  1231.                        (list 'warn
  1232.                              (concatenate 
  1233.                               'string
  1234.                               "~%[SM:DST] Type "
  1235.                               (symbol-name type)
  1236.                               " is being redefined (there were no instances)."))))
  1237.            (list 'destroy-type (list 'quote type)))))
  1238.  
  1239.      ;; Define instance structure. 
  1240.  
  1241.      (defstruct ,defstruct-type-and-options
  1242.        ,.(if documentation (list documentation))
  1243.        ,.defstruct-slot-definitions)
  1244.  
  1245.      ;; Create a type structure for the new type, and record the name.
  1246.  
  1247.      (setf (get ',type '$structure-type$)
  1248.            (record-new-type
  1249.             ,reusable          ',slot-access    ',macro-access
  1250.             ',uncomputed-slots ',computed-slots ',read-only-slots
  1251.             ',slot-defaults    ',slot-types     ',slot-info
  1252.             ',representation   ',initial-offset ',named
  1253.             ',creator          
  1254.         ;; Had a PDL overflow problem.  Apparently trying to save conses
  1255.         ;; by sharing.  We are saving the original macro call body in
  1256.         ;; a structure, which means that the expansion will have the
  1257.         ;; original form in it. Blew up in SUBLIS*, which tried to expand
  1258.         ;; the QUOTED call body when it found it in the expansion!
  1259.         ',#+TI (copy-list defining-form)
  1260.           #-TI defining-form
  1261.           ',type-info))
  1262.      (setq *structure-types*
  1263.            (sort (pushnew ',type *structure-types*)
  1264.                  #'(lambda (s1 s2) (string< (symbol-name s1) (symbol-name s2)))))
  1265.  
  1266.      ;; Define instance creator, intended for internal use so args are not 
  1267.      ;; keyworded, and are evaluated.  Computed slots have NO argument.
  1268.      ;;
  1269.      ;; (defun CREATE-<type> (name &optional (<slotname> <default>)...)
  1270.      ;;  "(CREATE-<type> name <slotname> ...)
  1271.      ;;  [Function] The slot arguments include only noncomputed slots.  Creates and
  1272.      ;;  records an instance of <type> ..."
  1273.  
  1274.      (defun ,creator
  1275.             (%name%
  1276.              &optional ,.(mapcar #'(lambda (s)
  1277.                                      (declare (symbol s))
  1278.                                      (list s (cdr (assoc s slot-defaults))))
  1279.                                  uncomputed-slots))
  1280.        ,(concatenate 'string
  1281.                      "CREATE-" type-string " <name> &optional"
  1282.                      (let ((*print-case* :downcase))
  1283.                        (format nil "~{ ~(<~A>~)~})" uncomputed-slots))
  1284.                      " [Function]
  1285.   The slot arguments include only uncomputed slots.  Creates and records
  1286.   an instance of " type-string " indexed by <name>."
  1287.                      (if sort-instances 
  1288.                        "
  1289.   The list of instance names returned by INSTANCES is kept sorted."
  1290.                        ""))
  1291.  
  1292.        ;; Type declarations.
  1293.        (declare (symbol %name%)
  1294.                 ,.(mapcar #'(lambda (s) 
  1295.                               (list 'type (cdr (assoc s slot-types)) s))
  1296.                           uncomputed-slots)
  1297.                 (optimize (safety 1) (space 2) (speed 3)))
  1298.  
  1299.        ;; Warn of redefinition if warnings not disabled.
  1300.        ;; NOTE that the existance test relies on proper disposal of destroyed
  1301.        ;; instances, to avoid the significantly slower test:
  1302.        ;;   (member name (structure-type-instances type-struct))
  1303.        ;; That is, this code relies on the <type> property of the instance name
  1304.        ;; symbol being destroyed when the instance is.
  1305.  
  1306.        (when (get %name% ',type)
  1307.          (if *warn-of-redefinitions*
  1308.            (warn ,(concatenate 'string
  1309.                                "~%[CREATE-" type-string "] Redefining instance ~A")
  1310.                  %name%))
  1311.          (destroys ',type %name%))
  1312.        
  1313.        ;; Creation of instance structure depends on whether the type is reusable.
  1314.        ;; For reusable types, the expansion will be of the form:
  1315.        ;;
  1316.        ;; (let* ((struct ; look on freelist and reuse if possible
  1317.        ;;          (if (freelist '<type>)
  1318.        ;;            (let ((old-struct (pop (freelist '<type>))))
  1319.        ;;              ;; Uncomputed slots are copied ...
  1320.        ;;              (setf (<type>-<slotname> old-struct) <slotname>) 
  1321.        ;;               . . .
  1322.        ;;              ;; Computed slots are set to default ...              
  1323.        ;;              (setf (<type>-<computed-slotname> old-struct) <initial-value>)
  1324.        ;;               . . .
  1325.        ;;              old-struct)
  1326.        ;;            (allocate-<type> <slotname-or-default> ...)))) ...)
  1327.        ;;
  1328.        ;; For UNreusable types:
  1329.        ;;
  1330.        ;; (let* ((struct (allocate-<type> <slotname-or-default> ...))) ...)
  1331.        ;;        
  1332.       
  1333.        (let* 
  1334.          ((struct 
  1335.            ,(if reusable
  1336.               (list 'if (list 'freelist (list 'quote type))
  1337.                     (cons 
  1338.                      'let
  1339.                      (cons
  1340.                       (list (list 'old-struct (list 'pop (list 'freelist 
  1341.                                                                (list 'quote type)))))
  1342.                       (append (mapcar           ; copy value of uncomputed slots
  1343.                                #'(lambda (s+a)
  1344.                                    (declare (cons s+a))
  1345.                                    (list 'setf
  1346.                                          (list (cdr s+a) 'old-struct)
  1347.                                          (car s+a)))
  1348.                                macro-access)
  1349.                               (mapcar           ; set default of computed slots
  1350.                                #'(lambda (s)
  1351.                                    (declare (symbol s))
  1352.                                    (list 'setf
  1353.                                          (list (cdr (assoc s slot-access))
  1354.                                                'old-struct)
  1355.                                          (cdr (assoc s slot-defaults))))
  1356.                                computed-slots)
  1357.                               '(old-struct))))
  1358.                     (cons maker
  1359.                           (mapcar
  1360.                            #'(lambda (sd)
  1361.                                (declare (list sd))
  1362.                                ;; use default value if computed (not provided as arg)
  1363.                                (if (member (first sd) computed-slots)
  1364.                                  (list 'quote (cdr (assoc (first sd) slot-defaults)))
  1365.                                  (first sd)))
  1366.                            defstruct-slot-definitions)))
  1367.               (cons maker
  1368.                     (mapcar
  1369.                      #'(lambda (sd)
  1370.                          (declare (list sd))
  1371.                          (if (member (first sd) computed-slots)
  1372.                            (cdr (assoc (first sd) slot-defaults))
  1373.                            (first sd)))
  1374.                      defstruct-slot-definitions)))))
  1375.          ;; Don't (declare (type <type> struct)) here as the type may be unnamed.
  1376.  
  1377.          ;; Record the structure under the instance name, and record the name,
  1378.          ;; sorting if requested.  Return the name.
  1379.  
  1380.          (setf (get %name% ',type) struct)
  1381.          ,(if sort-instances 
  1382.             (list 'insert-in-sorted-list '%name% 
  1383.                   (list 'instance-names (list 'quote type)))
  1384.             (list 'push '%name% (list 'instance-names (list 'quote type))))
  1385.          %name%))
  1386.  
  1387.      ;; Define no-eval macro to expand into instance creator. THE is used
  1388.      ;; for type checking since parameters are gone in expansion, so can't
  1389.      ;; be reset by CHECK-TYPE.
  1390.      ;;
  1391.      ;;  (defmacro <type> (name &key (<slotname> <default>) ...)
  1392.      ;;    "<type> name &key <slot1name> ... <slotNname>
  1393.      ;;    [Macro] Expands into CREATE-<type> call.  The ..."
  1394.      ;;    (list 'create-<type>
  1395.      ;;          (list 'quote name)
  1396.      ;;          (list 'the '<slottype> (list 'quote <slotname>) ...)))
  1397.  
  1398.      (defmacro ,type
  1399.                (%name% &key ,.(mapcar
  1400.                                #'(lambda (s) ; get defaults
  1401.                                    (declare (symbol s))
  1402.                                    (list s (cdr (assoc s slot-defaults))))
  1403.                                uncomputed-slots))
  1404.        ,(concatenate 'string
  1405.                      type-string " name &key"
  1406.                      (let ((*print-case* :downcase))
  1407.                        (format nil "~{ ~(:~A~)~})" uncomputed-slots))
  1408.                      " [Macro]
  1409.   Expands into " (symbol-name creator) " call.  The first argument
  1410.   is the name of the instance, and the remainder are optional keyword
  1411.   arguments for uncomputed slot values, using defaults if not given.")
  1412.  
  1413.        (list ',creator
  1414.              (list 'quote %name%)
  1415.              ,.(mapcar #'(lambda (s)
  1416.                            (declare (symbol s))
  1417.                            (if (eq (cdr (assoc s slot-types)) T)
  1418.                              ;; No THE needed.
  1419.                              (list 'list (list 'quote 'quote) s)
  1420.                              ;; Nontrivial type check with THE.
  1421.                              (list 'list 
  1422.                                    (list 'quote 'the)
  1423.                                    (list 'quote (cdr (assoc s slot-types)))
  1424.                                    (list 'list (list 'quote 'quote) s))))
  1425.                        uncomputed-slots)))
  1426.  
  1427.      ,.(if redefine (list '(restore-instances)))
  1428.  
  1429.      ',type))
  1430.  
  1431. ;;; For CCL users, indent this nicely:
  1432. #+:CCL (push (cons 'dst 1) ccl::*fred-special-indent-alist*)
  1433.  
  1434. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1435. ;;; Corresponding function.  Nearly identical, so refer to DST for comments.
  1436.  
  1437. (defun DEFINE-TYPE (type-and-options &rest slot-definitions
  1438.                       &aux defining-form               
  1439.                            defstruct-type-and-options
  1440.                            (defstruct-slot-definitions
  1441.                             (list :head))
  1442.                            type
  1443.                            (type-string "")
  1444.                            (documentation nil)
  1445.                            (reusable T)
  1446.                            (redefine nil)
  1447.                            (sort-instances nil)
  1448.                            (initial-offset 0)
  1449.                            (representation nil)
  1450.                            (named T)
  1451.                            (type-info nil)
  1452.                            (slot-access      (list :head))
  1453.                            (macro-access     (list :head))
  1454.                            (read-only-slots  (list :head))
  1455.                            (computed-slots   (list :head))
  1456.                            (uncomputed-slots (list :head))
  1457.                            (slot-defaults (list :head))
  1458.                            (slot-types (list :head))
  1459.                            (slot-info (list :head))
  1460.                            creator
  1461.                            maker
  1462.                            )
  1463.  
  1464.   "DEFINE-TYPE <type-and-options>  <slot-spec>*                     [Function]
  1465.  
  1466.   Functional version of DST.  Syntax identical EXCEPT that the
  1467.   arguments are EVALUTED.  See DST for full documentation.
  1468.  
  1469.   Note that functions and macros redefined by this function are NOT
  1470.   guaranteed to be compiled."
  1471.  
  1472.   (declare (symbol type reusable redefine creator maker)
  1473.            (simple-string type-string)
  1474.            (list defining-form slot-definitions defstruct-type-and-options 
  1475.                  defstruct-slot-definitions type-info slot-access macro-access 
  1476.                  read-only-slots computed-slots uncomputed-slots 
  1477.                  slot-defaults slot-info slot-types))
  1478.  
  1479.   ;; ------------------------------------------------------------------------
  1480.   ;; The first section sets up various lists, similar to those in DST.
  1481.   ;; ------------------------------------------------------------------------
  1482.  
  1483.   ;; Need this here but not in DST since only macros have &whole args.
  1484.   (setq defining-form (cons 'dst (cons type-and-options slot-definitions)))
  1485.   
  1486.   (make-names-of-things)
  1487.   (process-slot-definitions "DST")
  1488.   (process-type-definition  "DST")
  1489.  
  1490.   ;; ------------------------------------------------------------------------
  1491.   ;; The functional version constructs and evaluates the forms which would
  1492.   ;; have gone into the progn of DST, in the same order.
  1493.   ;; ------------------------------------------------------------------------
  1494.  
  1495.   ;; If this is a redefinition, make sure all the instances of the type
  1496.   ;; are disposed or saved, checking with or warning user as as appropriate.
  1497.   ;; (This is slightly different than the DST version since both redefine
  1498.   ;; cases must be included in the code, and of course there is no expansion.)
  1499.   
  1500.   (when (member type *structure-types*)
  1501.     (when redefine
  1502.       (save-instances type computed-slots uncomputed-slots)
  1503.       (if *warn-of-redefinitions*
  1504.         (warn "~%[SM:DEFINE-TYPE] ~S being redefined. ~A"
  1505.               type
  1506.               (if (instances type) 
  1507.                 "Instances temporarily saved on
  1508. *saved-instances*, and will be redefined as instances of the new type."
  1509.                 "(There were no instances.)")))
  1510.       (destroy-type type))
  1511.     (when (not redefine)
  1512.       (if *warn-of-redefinitions*
  1513.           (if (instances 'type)
  1514.             (cerror
  1515.              "Will proceed, calling DESTROY-TYPE first."
  1516.              "~%[SM:DEFINE-TYPE] Type ~S is defined, and has in-memory instances which~
  1517.               ~%would be destroyed by redefinition."
  1518.              type)
  1519.             (warn "~%[SM:DEFINE-TYPE] Type ~S is being redefined (there were no instances)."
  1520.                   type)))
  1521.       (destroy-type type)))
  1522.  
  1523.   ;; Define instance structure ... identical to DST except eval & backquote.
  1524.  
  1525.   (eval `(defstruct 
  1526.            ,defstruct-type-and-options 
  1527.            ,.(if documentation (list documentation))
  1528.            ,.defstruct-slot-definitions))
  1529.  
  1530.   ;; Create a type structure ... expanded form of DST with minor change #+TI
  1531.  
  1532.   (setf (get type '$structure-type$)
  1533.         (record-new-type
  1534.          reusable          slot-access    macro-access
  1535.          uncomputed-slots  computed-slots read-only-slots
  1536.          slot-defaults     slot-types     slot-info
  1537.          representation    initial-offset named
  1538.          creator           defining-form  type-info)) ; #+TI problem in macro only
  1539.   (setq *structure-types*
  1540.         (sort (pushnew type *structure-types*)
  1541.               #'(lambda (s1 s2) (string< (symbol-name s1) (symbol-name s2)))))
  1542.  
  1543.   ;; Define instance creator ... identical to DST except eval & backquote.
  1544.  
  1545.   (eval
  1546.    `(defun ,creator
  1547.            (%name%
  1548.             &optional ,.(mapcar #'(lambda (s)
  1549.                                     (declare (symbol s))
  1550.                                     (list s (cdr (assoc s slot-defaults))))
  1551.                                 uncomputed-slots))
  1552.       ,(concatenate 'string
  1553.                     "CREATE-" type-string " <name> &optional"
  1554.                     (let ((*print-case* :downcase))
  1555.                       (format nil "~{ ~(<~A>~)~})" uncomputed-slots))
  1556.                     " [Function]
  1557.   The slot arguments include only uncomputed slots.  Creates and records
  1558.   an instance of " type-string " indexed by <name>."
  1559.                      (if sort-instances 
  1560.                        "
  1561.   The list of instance names returned by INSTANCES is kept sorted."
  1562.                        ""))
  1563.       
  1564.       (declare (symbol %name%)
  1565.                ,.(mapcar #'(lambda (s) 
  1566.                              (list 'type (cdr (assoc s slot-types)) s))
  1567.                          uncomputed-slots)
  1568.                (optimize (safety 1) (space 2) (speed 3)))
  1569.       
  1570.       (when (get %name% ',type)
  1571.         (if *warn-of-redefinitions*
  1572.           (warn ,(concatenate 'string
  1573.                               "~%[CREATE-" type-string "] Redefining instance ~A")
  1574.                 %name%))
  1575.         (destroys ',type %name%))
  1576.       
  1577.       (let* 
  1578.         ((struct 
  1579.           ,(if reusable
  1580.              (list 'if (list 'freelist (list 'quote type))
  1581.                    (cons 
  1582.                     'let
  1583.                     (cons
  1584.                      (list (list 'old-struct (list 'pop (list 'freelist 
  1585.                                                               (list 'quote type)))))
  1586.                      (append (mapcar           ; copy value of uncomputed slots
  1587.                               #'(lambda (s+a)
  1588.                                   (declare (cons s+a))
  1589.                                   (list 'setf
  1590.                                         (list (cdr s+a) 'old-struct)
  1591.                                         (car s+a)))
  1592.                               macro-access)
  1593.                              (mapcar           ; set default of computed slots
  1594.                               #'(lambda (s)
  1595.                                   (declare (symbol s))
  1596.                                   (list 'setf
  1597.                                         (list (cdr (assoc s slot-access))
  1598.                                               'old-struct)
  1599.                                         (cdr (assoc s slot-defaults))))
  1600.                               computed-slots)
  1601.                              '(old-struct))))
  1602.                    (cons maker
  1603.                          (mapcar
  1604.                           #'(lambda (sd)
  1605.                               (declare (list sd))
  1606.                               (if (member (first sd) computed-slots)
  1607.                                 (list 'quote (cdr (assoc (first sd) slot-defaults)))
  1608.                                 (first sd)))
  1609.                           defstruct-slot-definitions)))
  1610.              (cons maker
  1611.                    (mapcar
  1612.                     #'(lambda (sd)
  1613.                         (declare (list sd))
  1614.                         (if (member (first sd) computed-slots)
  1615.                           (cdr (assoc (first sd) slot-defaults))
  1616.                           (first sd)))
  1617.                     defstruct-slot-definitions)))))
  1618.  
  1619.         (setf (get %name% ',type) struct)
  1620.         ,(if sort-instances 
  1621.            (list 'insert-in-sorted-list '%name% 
  1622.                  (list 'instance-names (list 'quote type)))
  1623.            (list 'push '%name% (list 'instance-names (list 'quote type))))
  1624.         %name%)))
  1625.  
  1626.   ;; Define no-eval macro ... identical to DST except eval and backquote.
  1627.  
  1628.   (eval
  1629.    `(defmacro ,type
  1630.               (%name% &key ,.(mapcar
  1631.                               #'(lambda (s) ; get defaults
  1632.                                   (declare (symbol s))
  1633.                                   (list s (cdr (assoc s slot-defaults))))
  1634.                               uncomputed-slots))
  1635.       ,(concatenate 'string
  1636.                     type-string " name &key"
  1637.                     (let ((*print-case* :downcase))
  1638.                       (format nil "~{ ~(:~A~)~})" uncomputed-slots))
  1639.                     " [Macro]
  1640.   Expands into " (symbol-name creator) " call.  The first argument
  1641.   is the name of the instance, and the remainder are optional keyword
  1642.   arguments for uncomputed slot values, using defaults if not given.")
  1643.       
  1644.       (list ',creator
  1645.             (list 'quote %name%)
  1646.             ,.(mapcar #'(lambda (s)
  1647.                           (declare (symbol s))
  1648.                           (if (eq (cdr (assoc s slot-types)) T)
  1649.                             (list 'list (list 'quote 'quote) s)
  1650.                             (list 'list 
  1651.                                   (list 'quote 'the)
  1652.                                   (list 'quote (cdr (assoc s slot-types)))
  1653.                                   (list 'list (list 'quote 'quote) s))))
  1654.                       uncomputed-slots))))
  1655.    
  1656.    (if redefine (restore-instances))
  1657.    type)
  1658.  
  1659. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1660. ;;;
  1661. ;;;                           SAVING AND LOADING
  1662. ;;;
  1663. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1664.  
  1665. (defun SAVE-TYPE (type &key (path nil) (style :pretty-macro) (omit ())
  1666.                        (define-type nil) (compile nil) (append nil)
  1667.                        (init-forms nil) (instances (instances type)))
  1668.   "save-type <type>                                                 [Function]
  1669.              &key :path :style :omit :define-type :compile 
  1670.                   :append :init-forms :instances
  1671.   Saves macro representations of instances of the type indicated.  If
  1672.   <path> is omitted, saved to the file loaded from, or a default path
  1673.   constructed from *default-instance-file-path*, the type's name, and 
  1674.   *default-instance-file-type*.  The <style> and <omit> parameters work
  1675.   as for PRINTS, though only :macro and :pretty-macro will result in re-
  1676.   loadable files.  If <define-type> is T, the form defining the type is
  1677.   placed at the front of the file.  If <compile> is T, the saved file 
  1678.   is compiled.  If <append> is T, <path> must be an existing file which
  1679.   is appended to (with no editing).  If <init-forms> is non-nil, it 
  1680.   should be a list of expressions.  These are written to the file after
  1681.   the in-package, but before anything else. If <instances> is supplied, 
  1682.   it should be a list of names of instances of type <type>, and only 
  1683.   these are saved; otherwise all instances are saved.  Returns the actual
  1684.   path used."
  1685.   (check-type type       symbol)
  1686.   (check-type path       (or null string pathname))
  1687.   (check-type style      keyword)
  1688.   (check-type omit       list)
  1689.   (check-type init-forms list)
  1690.   (check-type instances  list)
  1691.   (assert (member type (structure-types)) (type) "Unknown type")
  1692.   (if (null path)
  1693.     (setf path
  1694.           (let ((prev-path (get type '$SM-instance-path$)))
  1695.             (declare (type (or string pathname) prev-path))
  1696.             (if prev-path
  1697.               ;; Don't save to compiled file type if it was loaded
  1698.               ;; from the same!
  1699.               (make-pathname
  1700.                :device    (pathname-device prev-path)
  1701.                :directory (pathname-directory prev-path)
  1702.                :name      (pathname-name prev-path)
  1703.                :type      *default-instance-file-type*)
  1704.               ;; Otherwise let file system figure out type.
  1705.               (make-pathname
  1706.                :directory *default-instance-file-path*
  1707.                :name (symbol-name type)
  1708.                :type      *default-instance-file-type*)))))
  1709.   (let ((*package* (symbol-package type)))
  1710.     (with-open-file (stream path
  1711.                             :direction 
  1712.                 #-VAX :io     ; CCL and others need input for tabbing
  1713.                 #+VAX :output ; Vax can't do :IO if it doesn't exist!
  1714.                             :if-exists (if append :append :supersede)
  1715.                 :if-does-not-exist :create)
  1716.       (format stream ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1717. ;;; Instances of type ~A~%;;; Saved by SAVE-TYPE ~A~%;;; On ~A, a ~A"
  1718.               type
  1719.               (multiple-value-bind
  1720.                 (second minute hour date month year)
  1721.                 (get-decoded-time)
  1722.                 (declare (integer second minute hour date month year))
  1723.                 (format nil "~2,'0D-~A-~2,'0D ~2,'0D:~2,'0D:~2,'0D"
  1724.                         date 
  1725.                         (case month 
  1726.                           ((1) "Jan") ((2) "Feb") ((3) "Mar") ((4) "Apr")
  1727.                           ((5) "May") ((6) "Jun") ((7) "Jul") ((8) "Aug")
  1728.                           ((9) "Sep") ((10) "Oct") ((11) "Nov") ((12) "Dec"))
  1729.                         (- year 1900)
  1730.                         hour minute second))
  1731.               (machine-instance)
  1732.               (machine-type))
  1733.       (format stream "~%~%(in-package ~S)" (package-name (symbol-package type)))
  1734.       (when init-forms
  1735.         (format stream "~%")
  1736.         (dolist (form init-forms) (format stream "~%~S" form)))
  1737.       (if define-type
  1738.       (let ((*print-pretty* t) (*print-escape* t) (*print-circle* nil) 
  1739.                 (*print-case* :upcase) (*print-array* t) 
  1740.                 #+:ccl (ccl::*print-structure* t) (dst-form (defining-form type)))
  1741.             (format stream "~%~%(~S ~A~{~&  ~S~})"
  1742.                     (first dst-form) 
  1743.                     (prin1-to-string (second dst-form))
  1744.                     (cddr dst-form))))
  1745.       (dolist (i (sort (copy-list instances)
  1746.                        #'(lambda (s1 s2)
  1747.                            (string<= (symbol-name s1) (symbol-name s2)))))
  1748.         (declare (symbol i))
  1749.         (format stream "~&~%")
  1750.         (prints type i :style style :omit omit :stream stream))
  1751.       (format stream "~&~%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1752. ;;; EOF~%")))
  1753.   ;; Now that it worked, safe to update path used.
  1754.   (setf (get type '$SM-instance-path$) path
  1755.         *default-instance-file-path* (pathname-directory path))
  1756.   (if compile 
  1757.     #+:ccl (ccl:eval-enqueue `(compile-file ,(namestring path)))
  1758.     #-:ccl (compile-file path)
  1759.     )
  1760.   path)
  1761. (proclaim '(function save-type 
  1762.             (symbol &key (or null string pathname) keyword list t t list)
  1763.             pathname))
  1764.  
  1765. (defun LOAD-TYPE (type &key (path nil))
  1766.   "load-type <type>  &key :path                                     [Function]
  1767.   Loads the file indicated in <path>, which presumably has macro definitions
  1768.   of instances of <type>, and records the path name.  If <path> is nil,
  1769.   guesses at a path name based on previous loads/saves, or on the type.
  1770.   The <type> need not be defined yet, if the file defines it.  If an
  1771.   :after-load type option has been specified, the expression stored there
  1772.   is evaluated after the file is loaded."
  1773.   (check-type type symbol)
  1774.   (check-type path (or null string pathname))
  1775.   (if (null path)
  1776.     (setf path
  1777.           (or (get type '$SM-instance-path$)
  1778.               (make-pathname
  1779.                :directory *default-instance-file-path*
  1780.            :name (symbol-name type)))))
  1781.   (let ((*package* (symbol-package type)))
  1782.     #+HP (format T "~&; Loading ~A ..." path)
  1783.     (load path))
  1784.   ;; Make sure the type was indeed defined.
  1785.   (assert (member type (structure-types)) (type)
  1786.           "Type ~A still not defined after LOAD-TYPE." type)
  1787.   ;; Now that it worked, safe to record the new path for future defaults.
  1788.   (setf (get type '$SM-instance-path$) path
  1789.         *default-instance-file-path* (pathname-directory path))
  1790.   ;; Run :after-load method, if present.
  1791.   (let ((after-load (type-info type :after-load)))
  1792.     (if after-load (funcall after-load)))
  1793.   path)
  1794. (proclaim '(function load-type (symbol &key (or null string pathname))))
  1795.  
  1796. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1797. (provide :SM)
  1798. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1799. ;;; EOF
  1800.